//
//  SwipeCollectionCell.swift
//  TSMediaPlayer
//
//  Created by TSYH on 2024/6/17.
//

import UIKit
import ExtensionsKit

public protocol SwipeCollectionCellDataSource {
    func swipeEnable(for collectionCell: SwipeCollectionCell) -> Bool
    func swipeActions(for collectionCell: SwipeCollectionCell) -> [SwipeAction]?
    func swipeCell(_ collectionCell: SwipeCollectionCell, didSelected action: SwipeAction)
}

public protocol SwipeAction {
    var swipeAttributedTitle: NSAttributedString? { get }
    var swipeIcon: UIImage? { get }
    var swipeBackgroundColor: UIColor? { get }
}

open class SwipeCollectionCell: UICollectionViewCell {
    public var swipDataSource: SwipeCollectionCellDataSource?
    
    lazy var swipeContentView = UIView()
    
    lazy var swipePanGesture: UIPanGestureRecognizer = {
        let pan = UIPanGestureRecognizer(target: self, action: #selector(panGestureChanged(_:)))
        pan.delegate = self
        return pan
    }()
    
    lazy var tapGesture: UITapGestureRecognizer = {
        let tap = UITapGestureRecognizer(target: self, action: #selector(onTapGesture(_:)))
        tap.isEnabled = false
        return tap
    }()
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        _setupUI()
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func _setupUI() {
        swipeContentView.backgroundColor = .red
        addSubview(swipeContentView)
        
        addGestureRecognizer(swipePanGesture)
        contentView.addGestureRecognizer(tapGesture)
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
//        swipeContentView.frame = CGRect(x: bounds.width, y: 0, width: 160, height: bounds.height)
    }
    
    public override func prepareForReuse() {
        super.prepareForReuse()
        isMenuExpanded = false
        swipeContentView.subviews.forEach({ $0.removeFromSuperview() })
    }
    
    // 滑动中
    private lazy var isSwipping = false
    // 侧滑菜单展开中
    private lazy var isMenuExpanded = false {
        didSet {
            if isMenuExpanded != oldValue {
                updateFinalPosition(isExpand: isMenuExpanded, animated: false)
            }
            tapGesture.isEnabled = isMenuExpanded
        }
    }
    
    @objc private  func onTapGesture(_ gesture: UITapGestureRecognizer) {
        isMenuExpanded = false
    }
    
    @objc private  func panGestureChanged(_ gesture: UIPanGestureRecognizer) {
        let velocity = gesture.velocity(in: self)
        
        switch gesture.state {
        case .began:
            Log("===: \(velocity.x) - \(velocity.y)")
            // 指定向左时响应
            if abs(velocity.x) >= abs(velocity.y) {
                isSwipping = true
                renderActions()
            }
            
        case .changed:
            guard isSwipping else { return }
            let trans = gesture.translation(in: gesture.view)
            
            let enable = isRTL ? trans.x > 0 : trans.x < 0
            // 禁止开始时向右滑
            if contentView.x == 0, !enable {
                return
            }
            
            let maxMoveX: CGFloat = swipeContentView.width
            if isRTL {
                let targetX = swipeContentView.x + trans.x
                swipeContentView.x = min(targetX, 0)
                contentView.x = swipeContentView.frame.maxX
            } else {
                let targetX = swipeContentView.x + trans.x
                swipeContentView.x = max(targetX, bounds.width - maxMoveX)
                contentView.x = swipeContentView.x - bounds.width
            }
            gesture.setTranslation(.zero, in: gesture.view)
            Log("===: \(contentView.x)")
            
        case .ended:
            isSwipping = false
            let x = swipeContentView.frame.origin.x
            let outOfHalf = isRTL ? x > -(swipeContentView.width/2.0) : bounds.width - x >= swipeContentView.width/2.0
            if outOfHalf {
                updateFinalPosition(isExpand: true, animated: true)
            } else {
                updateFinalPosition(isExpand: false, animated: true)
            }
        default:
            break
        }
    }
    
    // 更新最终位置
    private func updateFinalPosition(isExpand: Bool, animated: Bool) {
        if isExpand {
            let moveX = isRTL ? self.swipeContentView.width : -self.swipeContentView.width
            UIView.animate(withDuration: animated ? 0.2 : 0) {
                self.contentView.x = moveX
                self.swipeContentView.x = self.isRTL ? 0 : self.bounds.width - self.swipeContentView.width
            } completion: { _ in
                self.isMenuExpanded = true
            }
        } else {
            let positionX = isRTL ? -swipeContentView.width : bounds.width
            UIView.animate(withDuration: animated ? 0.2 : 0, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 10, options: .curveEaseIn) {
                self.contentView.x = 0
                self.swipeContentView.x = positionX
            } completion: { _ in
                self.isMenuExpanded = false
            }
        }
    }
    
    private func renderActions() {
        guard swipeContentView.subviews.isEmpty, let dataSource = swipDataSource, let actions = dataSource.swipeActions(for: self) else {
            return
        }
        let w = 74.0 * CGFloat(actions.count)
        if isRTL {
            swipeContentView.frame = CGRect(x: -w, y: 0, width: w, height: bounds.height)
        } else {
            swipeContentView.frame = CGRect(x: bounds.width, y: 0, width: w, height: bounds.height)
        }
        
        let items = isRTL ? actions.reversed() : actions
        for (index, action) in items.enumerated() {
            let itemV = SwipeActionView(frame: CGRect(x: CGFloat(index)*74.0, y: 0, width: 74, height: swipeContentView.frame.height), action: action)
            swipeContentView.addSubview(itemV)
            itemV.addTarget(self, action: #selector(itemClick(_:)), for: .touchUpInside)
        }
    }
    
    @objc private  func itemClick(_ sender: SwipeActionView) {
        swipDataSource?.swipeCell(self, didSelected: sender.action)
    }
    
    public func resetState() {
        isMenuExpanded = false
    }
    
    var isRTL: Bool {
        return semanticContentAttribute == .forceRightToLeft
    }
}

extension SwipeCollectionCell: UIGestureRecognizerDelegate {
    public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer == swipePanGesture {
            guard let dataSource = swipDataSource, dataSource.swipeEnable(for: self) else {
                return false
            }
            let velocity = swipePanGesture.velocity(in: self)
            if abs(velocity.x) >= abs(velocity.y) {
                return true
            }
            return false
        }
        return super.gestureRecognizerShouldBegin(gestureRecognizer)
    }
}

class SwipeActionView: UIButton {
    
    var action: SwipeAction
    required init(frame: CGRect, action: SwipeAction) {
        self.action = action
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    lazy var textLabel = UILabel()
    lazy var iconView = UIImageView()
    func setupUI() {
        backgroundColor = action.swipeBackgroundColor
        addSubview(textLabel)
        addSubview(iconView)
        textLabel.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 20)
        iconView.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
        
        var txtCenterY: CGFloat = bounds.height/2.0
        if action.swipeIcon != nil {
            txtCenterY = txtCenterY+12
        }
        textLabel.center = CGPoint(x: bounds.width/2.0, y: txtCenterY)
        
        var icCenterY: CGFloat = bounds.height/2.0
        if action.swipeAttributedTitle != nil {
            icCenterY = icCenterY-12
        }
        iconView.center = CGPoint(x: bounds.width/2.0, y: icCenterY)
        
        textLabel.isHidden = action.swipeAttributedTitle == nil
        textLabel.attributedText = action.swipeAttributedTitle
        
        iconView.isHidden = action.swipeIcon == nil
        iconView.image = action.swipeIcon
    }
}
